﻿using NightskyMimic.Domain;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NightskyMimic.Core
{
    public class State
    {
        private List<Creature> _tapedCreatures;
        private List<Creature> _tapedOpponentCreatures;

        static int count = 1;

        public int Life { get; set; }

        public List<Creature> Creatures { get; set; }

        public int OpponentLife { get; set; }

        public List<Creature> OpponentCreatures { get; set; }

        public State Parent { get; set; }

        public List<Attack> Attack { get; set; }

        public List<Block> Block { get; set; }

        public int Id { get; private set; }

        public List<State> Children { get; set; }

        public virtual bool isTerminal { 
            get{ return Life <= 0 || OpponentLife <= 0; }
        }

        public virtual double Utility 
        {
            get 
            {
                if (Life <= 0)
                    return double.NegativeInfinity;

                if (OpponentLife <= 0)
                    return double.PositiveInfinity;


                return (lifeValue(Life)*0.5 + creaturesValue(Creatures)*0.5) - (lifeValue(OpponentLife)*0.5 + creaturesValue(OpponentCreatures)*0.5); 
            }
        }

        private double lifeValue(int life)
        {
            return 1 - (1 / (double)life);
        }

        private double creaturesValue(List<Creature> creatures) 
        {
            if (creatures.Count == 0)
                return 0.0;

            double result = 0;

            foreach (Creature c in creatures) 
            {
                result += (c.Power + c.Toughness);
            }

            return 1 - 1/(double)result;
        }

        public List<Creature> RightCreatures(bool isMaxTurn) 
        {
            Creatures.ForEach(x => x.Tapped = false);
            OpponentCreatures.ForEach(x => x.Tapped = false);

            //if is min turn, we can't untap the max attacking creatures  
            if (!isMaxTurn)
            {
                foreach (Creature c in Creatures)
                {
                    if (_tapedCreatures.Contains(c))
                        c.Tapped = true;
                }
            }

            else
            {
                foreach (Creature c in OpponentCreatures)
                {
                    if (_tapedOpponentCreatures.Contains(c))
                        c.Tapped = true;
                }
            }

            if (isMaxTurn)
                return Creatures.Where(x => !x.Tapped).ToList();
            else
                return OpponentCreatures.Where(x => !x.Tapped).ToList();
        }

        public State(int life, List<Creature> creatures, int opponentLife, List<Creature> opponentCreatures, List<Block> blocks, List<Attack> attacks, State parent)
        {
            Life = life;
            Creatures = creatures;
            OpponentLife = opponentLife;
            OpponentCreatures = opponentCreatures;
            Attack = attacks;
            Block = blocks;
            Parent = parent;
            Id = count;
            Children = new List<State>();
            Depth = parent == null ? 0 : parent.Depth + 1;
            count++;
            tapedCreatures();
        }

        private void tapedCreatures()
        {
            if (Creatures != null && OpponentCreatures != null)
            {
                _tapedCreatures = Creatures.Where(x => x.Tapped).ToList();
                _tapedOpponentCreatures = OpponentCreatures.Where(x => x.Tapped).ToList();
            }
        }

        public override string ToString()
        {
            string result = Id + " ";
            if ((Attack == null || Attack.Count == 0) && (Block == null || Block.Count == 0))
                return Id + " Nop" + " (" + Utility/*.ToString("0.000")*/ + ")";

            if (Block != null && Block.Count != 0)
            {
                foreach (Block b in Block)
                {
                    result += b.ToString() + "\n";
                }
            }

            if (Attack != null && Attack.Count != 0)
            {
                foreach (Attack a in Attack)
                {
                    result += a.ToString() + "\n";
                }
                result = result.Remove(result.Length - 1);
            }
 
            return result + "(" +  Utility/*.ToString("0.000")*/ + ")";
        }

        public int Depth { get; private set; }

    }
}